package main

import (
	"bufio"
	"fmt"
	"github.com/google/gopacket"
	"github.com/google/gopacket/layers"
	"github.com/google/gopacket/pcap"
	"net"
	"os"
	"time"
)

/**
  基于libpcap+gopacket 抓包解包发包
  单个IP阻断运行命令 ./libpcapAccess.go [要阻断的IP] [BPF]
*/

var handleMirror *pcap.Handle
var handleSend *pcap.Handle
var err error
var ip net.IP
var parser gopacket.DecodingLayerFunc
var decoded []gopacket.LayerType

func main() {
	fmt.Println("pcap version: ", pcap.Version())
	// 获取命令行参数
	//1.0 获取第二个参数,设置阻断IP,单个
	ip = net.ParseIP(os.Args[1])
	if ip == nil {
		panic("please enter the correct ip not " + ip.String())
	}
	//2.0 获取第三个参数,设置BPF
	bpf := os.Args[2]
	//3.0 读取控制台输入的网卡
	devs, _ := pcap.FindAllDevs()
	fmt.Println("Please enter the network card, for example eth0")
	for i := range devs {
		fmt.Println(devs[i].Name)
	}
	fmt.Println("Please enter receive:")
	in := bufio.NewReader(os.Stdin)
	str := make([]byte, 1024)
	str, _, err = in.ReadLine()
	if err != nil {
		panic(err)
	}
	deviceId := string(str)
	ip = net.ParseIP(os.Args[1])
	if ip == nil {
		panic("please enter the correct ip not " + ip.String())
	}
	handleMirror, err = pcap.OpenLive(
		deviceId, // network device
		int32(65535),
		true,
		time.Microsecond,
	)
	if err != nil {
		fmt.Println("Mirror Handler error", err.Error())
		return
	}
	fmt.Println("Please enter send:")
	in = bufio.NewReader(os.Stdin)
	str = make([]byte, 1024)
	str, _, err = in.ReadLine()
	if err != nil {
		panic(err)
	}
	deviceId = string(str)
	handleSend, err = pcap.OpenLive(
		deviceId, // network device
		int32(65535),
		false,
		time.Microsecond,
	)
	if err != nil {
		fmt.Println("Mirror Handler error", err.Error())
		return
	}

	err := handleMirror.SetBPFFilter(bpf)
	if err != nil {
		return
	}
	fmt.Println("The program has been started")
	fmt.Println("当前阻断IP:", ip)
	fmt.Println("当前BPF:", bpf)
	fmt.Println("当前deviceId:", deviceId)
	source := gopacket.NewPacketSource(handleMirror, handleMirror.LinkType())
	for packet := range source.Packets() {
		go analysePacket(packet)
	}
}

func analysePacket(packet gopacket.Packet) {
	ethlayer := packet.Layer(layers.LayerTypeEthernet)
	eth := ethlayer.(*layers.Ethernet)
	ipv4layer := packet.Layer(layers.LayerTypeIPv4)
	ipv4 := ipv4layer.(*layers.IPv4)
	tcplayer := packet.Layer(layers.LayerTypeTCP)
	tcp := tcplayer.(*layers.TCP)
	fmt.Println("1srcIp: ", ipv4.SrcIP.String(), "dstIp: ", ipv4.DstIP.String())
	if !ip.Equal(ipv4.SrcIP) {
		return
	}
	fmt.Println("2srcIp: ", ipv4.SrcIP.String(), "dstIp: ", ipv4.DstIP.String())
	//5.0 过滤掉SYN FIN RST 和 http
	if tcp.SYN || tcp.FIN || tcp.RST {
		//log.Println("send nothing")
		return
	}

	//握手完成开始推送数据时候拦截
	win := tcp.Window
	loop := 5
	count := loop
	for count > 0 {
		//阻断主要是发送的SEQ序列号和上一位的ACK值相等
		seq := tcp.Ack + uint32(uint16(loop-count)*win)
		err = SendFlag(eth, ipv4, tcp, eth.DstMAC, eth.SrcMAC, ipv4.DstIP, ipv4.SrcIP, tcp.DstPort, tcp.SrcPort, seq, 10000, false, false, true)
		if err != nil {
			fmt.Println(err)
		}
		count = count - 1
	}
	fmt.Println("send rst")
}

func SendFlag(eth *layers.Ethernet, ipv4 *layers.IPv4, tcp *layers.TCP, srcMac, dstMac net.HardwareAddr, srcIp, dstIp net.IP, srcPort, dstPort layers.TCPPort, seq, ack uint32, SYN, ACK, RST bool) error {
	//fds, _ := syscall.Socket(syscall.AF_INET, syscall.SOCK_RAW, syscall.IPPROTO_IP)
	eth.SrcMAC = srcMac
	mac, _ := net.ParseMAC("b8-4d-ee-f0-fb-24")
	eth.DstMAC = mac
	ipv4.SrcIP = net.ParseIP("180.101.49.12")
	ipv4.DstIP = net.ParseIP("192.168.2.9")
	tcp.SrcPort = srcPort
	tcp.DstPort = dstPort
	tcp.SYN = SYN
	tcp.Seq = seq
	tcp.ACK = ACK
	tcp.Ack = ack
	tcp.RST = RST

	options := gopacket.SerializeOptions{
		FixLengths:       true,
		ComputeChecksums: true,
	}

	if err = tcp.SetNetworkLayerForChecksum(ipv4); err != nil {
		panic(err)
	}
	buffer := gopacket.NewSerializeBuffer()

	err = gopacket.SerializeLayers(buffer, options, eth, ipv4, tcp)
	if err != nil {
		panic(err)
	}
	err = handleSend.WritePacketData(buffer.Bytes())
	//var addr syscall.SockaddrInet4
	//addr.Addr = [4]byte{192, 168, 7, 45}
	//port := dstPort
	//addr.Port = int(port)
	//err = syscall.Sendto(fds, buffer.Bytes(), 0, &addr)
	if err != nil {
		return err
	}
	fmt.Println("3srcIp: ", ipv4.SrcIP.String(), "dstIp: ", ipv4.DstIP.String())
	fmt.Println("3srcMac: ", eth.SrcMAC.String(), "dstMac: ", eth.DstMAC.String())
	return nil
}
